﻿#region usings
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using System.Threading;
using Ionic.Utils.Zip;
#endregion

namespace OOMetaExtractor
{
    public partial class FormPrincipal : Form
    {
        public static string MSG_DTD_NOT_FOUND;
        public static string MSG_ERROR_OCURRED;
        public static string MSG_CLEANING_METADATA_OF_FILE;
        public static string MSG_CLEANING_CANCELLED;
        public static string MSG_METADATA_CLEANED_FILES;
        public static string MSG_METADATA_CLEANED_ALL_FILES;
        public static string MSG_METADATA_CLEANED;
        public static string MSG_SUMMARIZING_METADATA;
        public static string MSG_SUMMARY_CANCELLED;
        public static string MSG_SUMMARY_FINISH_X_FILES;
        public static string MSG_SUMMARY_FINISH_ALL_FILES;
        public static string MSG_SUMMARY_FINISH;
        public static string MSG_TOO_MANY_FILES;
        public static string MSG_TOO_MANY_FILES_TITLE;
        public static string MSG_CLEANING_METADATA;
        public static string MSG_CANCEL;
        public static string MSG_SUMMARIZING_FOLDER;

        // Extensiones soportadas
        public String extensiones = "*.sxw;*.odt;*.ods;*.odg;*.odp";
        // Lista de ficheros abiertos en cada pestaña
        public List<String> ficheros = new List<String>();
        // Carpeta usada temporalmente para la extracción del contenido del documento
        private static String tempFolder = Path.GetFullPath(Application.StartupPath) + @"\temp\";

        #region Definición de tipos delegate, usados para crear los hilos
        delegate void backgroundWorker_RunWorkerCompleted_CallBack(object sender, RunWorkerCompletedEventArgs e);
        delegate void setProgressBar_Maximum_CallBack(ProgressBar pb, int max);
        delegate void setLabel_Text_CallBack(Label l, String text);
        delegate void setTabPage_Text_CallBack(TabPage t, String text);
        #endregion

        #region Eventos del formulario
        public FormPrincipal()
        {
            InitializeComponent();
            this.Text = String.Format("{0} - {1}", Application.ProductName, Application.ProductVersion.Substring(0, 5));
        }

        private void FormPrincipal_Load(object sender, EventArgs e)
        {
            if (!Directory.Exists(Path.GetFullPath(Application.StartupPath) + @"\DTD\"))
            {
                MessageBox.Show(String.Format(MSG_DTD_NOT_FOUND, Path.GetFullPath(Application.StartupPath)), "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }
        #endregion

        #region Eventos del menu superior

        /*Evento producido al mostrarse el menu, lo usamos para desactivar los submenus que no esten disponibles*/
        private void menuStrip_MenuActivate(object sender, EventArgs e)
        {
            ((ToolStripMenuItem)menuStripPrincipal.Items["accionesToolStripMenuItem"]).DropDownItems["borrarEstaPestañaToolStripMenuItem"].Enabled = (tabControl.TabCount != 0 && tabControl.SelectedTab != null && tabControl.SelectedTab.Controls[0].GetType() == typeof(ListBox));
            ((ToolStripMenuItem)menuStripPrincipal.Items["pestañasToolStripMenuItem"]).DropDownItems["cerrarPestañaToolStripMenuItem"].Enabled = (tabControl.TabCount != 0 && tabControl.SelectedTab != null);
            ((ToolStripMenuItem)menuStripPrincipal.Items["accionesToolStripMenuItem"]).DropDownItems["borrarTodasLasPestañasToolStripMenuItem"].Enabled = (tabControl.TabCount != 0);
            ((ToolStripMenuItem)menuStripPrincipal.Items["pestañasToolStripMenuItem"]).DropDownItems["cerrarTodasLasPestañasToolStripMenuItem"].Enabled = (tabControl.TabCount != 0);
        }

        #region Eventos de menú Archivo
        private void abrirArchivoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                añadirFichero(this.openFileDialog.FileName, this.tabControl);
            }
        }

        private void abrirCarpetaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                añadirDirectorio(folderBrowserDialog.SelectedPath, this.tabControl);
            }
        }

        private void limpiarCarpetaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                borrarMetadatosCarpeta(folderBrowserDialog.SelectedPath, this.tabControl);
            }
        }

        private void generarResumenDeCarpetaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    resumirMetadatosCarpeta(new String[]{folderBrowserDialog.SelectedPath, 
                                                         saveFileDialog.FileName}, 
                                            this.tabControl);
                }
            }
        }

        private void cerrarToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }
        #endregion

        #region Eventos del menú Metadatos
        private void borrarEstaPestañaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            borrarMetadatosDeFichero(ficheros[tabControl.SelectedIndex], (ListBox)tabControl.SelectedTab.Controls[0]);
        }

        private void borrarTodasLasPestañasToolStripMenuItem_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < ficheros.Count; i++)
            {
                if (tabControl.TabPages[i].Controls[0].GetType() == typeof(ListBox))
                {
                    borrarMetadatosDeFichero(ficheros[i], (ListBox)tabControl.TabPages[i].Controls[0]);
                }
            }
        }
        #endregion

        #region Eventos del menú Pestañas
        private void cerrarPestañaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (tabControl.SelectedIndex != -1)
            {
                ficheros.RemoveAt(tabControl.SelectedIndex);
                tabControl.TabPages.RemoveAt(tabControl.SelectedIndex);
            }
        }

        private void cerrarTodasLasPestañasToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tabControl.TabPages.Clear();
            ficheros.Clear();
        }
        #endregion

        #region Evento del menu Opciones
        private void opcionesToolStripMenutem_Click(object sender, EventArgs e)
        {
            Program.FormOpcionesInstance.ShowDialog();
        }
        #endregion

        #region Evento del menu Sobre...
        private void sobreToolStripMenuItem_Click(object sender, EventArgs e)
        {
            (new AboutBox()).ShowDialog();
        }
        #endregion

        #endregion

        #region Eventos del componente backgroundWorkerBorrarCarpeta
        private void backgroundWorkerBorrarCarpeta_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = e.Argument;
            borrarTodosLosMetadatos((BackgroundWorker)sender, (TabPage)e.Argument);
        }

        private void backgroundWorkerBorrarCarpeta_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new backgroundWorker_RunWorkerCompleted_CallBack(backgroundWorkerBorrarCarpeta_RunWorkerCompleted), 
                            new Object[]{ sender, e });
                return;
            }
            if (e.Error != null)
            {
                MessageBox.Show(MSG_ERROR_OCURRED + ": " + e.Error.Message,
                                "Error",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
            }
            else
            {
                ((Button)((TabPage)e.Result).Controls[2]).Visible = false;
            }
        }

        /*Función usada asincronamente mediante backgroundWorkerBorrarCarpeta
         Todas las modificaciones de la interfaz se realizan por el hilo principal*/
        private void borrarTodosLosMetadatos(BackgroundWorker bw, TabPage tab)
        {
            List<String> ls = new List<String>();
            foreach (String extension in extensiones.Split(';'))
            {
                ls.AddRange(Directory.GetFiles(ficheros[tab.TabIndex], extension));
            }
            Invoke(new setProgressBar_Maximum_CallBack(setProgressBar_Maximum),
                   new Object[] { (ProgressBar)tab.Controls[0], 
                                  ls.Count});
            for (int i = 0; i < ls.Count; i++)
            {
                Invoke(new setLabel_Text_CallBack(setLabel_Text),
                       new Object[] { (Label)tab.Controls[1], 
                                      MSG_CLEANING_METADATA_OF_FILE + " " + Path.GetFileName(ls[i])});
                Invoke(new MethodInvoker(((ProgressBar)tab.Controls[0]).PerformStep));
                borrarMetadatosDeFichero(ls[i], null);
                if (backgroundWorkerBorrarCarpeta.CancellationPending)
                {
                    Invoke(new setTabPage_Text_CallBack(setTabPage_Text),
                           new Object[] { (TabPage)tab, 
                                          MSG_CLEANING_CANCELLED});
                    Invoke(new setLabel_Text_CallBack(setLabel_Text),
                           new Object[] { (Label)tab.Controls[1], 
                                          String.Format(MSG_METADATA_CLEANED_FILES, (i + 1).ToString())});
                    return;
                }
            }
            Invoke(new setLabel_Text_CallBack(setLabel_Text),
                   new Object[] { (Label)tab.Controls[1], 
                                  MSG_METADATA_CLEANED_ALL_FILES});
            Invoke(new setTabPage_Text_CallBack(setTabPage_Text),
                   new Object[] { (TabPage)tab, 
                                  MSG_METADATA_CLEANED});
        }

        /*Evento del boton cancel que creamos dinámicamente*/
        public void buttonCancel_Click(object sender, EventArgs e)
        {
            backgroundWorkerBorrarCarpeta.CancelAsync();
        }
        #endregion

        #region Eventos del componente backgroundWorkerResumirCarpeta
        private void backgroundWorkerResumirCarpeta_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = e.Argument;
            resumirCarpeta((BackgroundWorker)sender, (Object[])e.Argument);
        }

        private void backgroundWorkerResumirCarpeta_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new backgroundWorker_RunWorkerCompleted_CallBack(backgroundWorkerResumirCarpeta_RunWorkerCompleted),
                            new Object[] { sender, e });
                return;
            }
            if (e.Error != null)
            {
                MessageBox.Show(MSG_ERROR_OCURRED + ": " + e.Error.Message,
                                "Error",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
            }
            else
            {
                Object[] args = (Object[])e.Result;
                ((Button)(((TabPage)args[0]).Controls[2])).Visible = false;
            }
        }

        /*Función usada asincronamente mediante backgroundWorkerResumirCarpeta
         Todas las modificaciones de la interfaz se realizan por el hilo principal*/
        private void resumirCarpeta(BackgroundWorker bw, Object[] args)
        {
            TabPage tab = (TabPage)args[0];
            String[] paths = (string[])args[1];
            String DocumentFolder = paths[0];
            String ResumenFile = paths[1];
            List<String> ls = new List<String>();
            foreach (String extension in extensiones.Split(';'))
            {
                ls.AddRange(Directory.GetFiles(DocumentFolder, extension));
            }
            Invoke(new setProgressBar_Maximum_CallBack(setProgressBar_Maximum),
                   new Object[] { (ProgressBar)tab.Controls[0], 
                                  ls.Count});
            FileInfo fi = new FileInfo(ResumenFile);
            TextWriter tw = fi.CreateText();
            for (int i = 0; i < ls.Count; i++)
            {
                Invoke(new setLabel_Text_CallBack(setLabel_Text),
                       new Object[] { (Label)tab.Controls[1], 
                                      MSG_SUMMARIZING_METADATA + " " + Path.GetFileName(ls[i])});
                Invoke(new MethodInvoker(((ProgressBar)tab.Controls[0]).PerformStep));
                tw.WriteLine(resumeFichero(ls[i]));
                if (backgroundWorkerBorrarCarpeta.CancellationPending)
                {
                    Invoke(new setTabPage_Text_CallBack(setTabPage_Text),
                           new Object[] { (TabPage)tab, 
                                          MSG_SUMMARY_CANCELLED});
                    Invoke(new setLabel_Text_CallBack(setLabel_Text),
                           new Object[] { (Label)tab.Controls[1], 
                                          String.Format(MSG_SUMMARY_FINISH_X_FILES, (i + 1))});
                    return;
                }
            }
            tw.Close();
            Invoke(new setLabel_Text_CallBack(setLabel_Text),
                   new Object[] { (Label)tab.Controls[1], 
                                  MSG_SUMMARY_FINISH_ALL_FILES});
            Invoke(new setTabPage_Text_CallBack(setTabPage_Text),
                   new Object[] { (TabPage)tab, 
                                  MSG_SUMMARY_FINISH});
        }
        #endregion

        #region Funciones usadas para actualizar la interfaz desde un hilo externo
        private void setProgressBar_Maximum(ProgressBar pb, int max)
        {
            pb.Maximum = max;
        }

        private void setLabel_Text(Label l, String text)
        {
            l.Text = text;
        }

        private void setTabPage_Text(TabPage t, String text)
        {
            t.Text = text;
        }
        #endregion

        #region Funciones del menu contextual para copiar los metadatos
        /*Evento que se produce antes de mostrar el ContextMenu*/
        private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            contextMenuStripListView.Items["copiarEstaLineaToolStripMenuItem"].Enabled = (((ListBox)contextMenuStripListView.SourceControl).Items.Count != 0 && ((ListBox)contextMenuStripListView.SourceControl).SelectedItem != null);
            contextMenuStripListView.Items["copiarTodoToolStripMenuItem"].Enabled = (((ListBox)contextMenuStripListView.SourceControl).Items.Count != 0);
        }

        //Copia al portapapeles la linea seleccionada del listbox
        private void copiarEstaLineaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(((ListBox)contextMenuStripListView.SourceControl).SelectedItem.ToString());
        }
        
        //Copia al portapapeles todos los items del listbox
        private void copiarTodoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            String todo = "";
            for (int i = 0; i < ((ListBox)contextMenuStripListView.SourceControl).Items.Count; i++)
            {
                todo += ((ListBox)contextMenuStripListView.SourceControl).Items[i].ToString() + "\n\r";
            }
            Clipboard.SetText(todo);
        }
        #endregion

        #region Eventos para arrastrar y soltar ficheros y carpetas
        private void FormPrincipal_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy: DragDropEffects.None;
        }

        private void FormPrincipal_DragDrop(object sender, DragEventArgs e)
        {
            string [] ficheros = (String[])e.Data.GetData(DataFormats.FileDrop);
            foreach (String fichero in ficheros)
            {
                if (Directory.Exists(fichero))
                {
                    añadirDirectorio(fichero, this.tabControl);
                }
                else
                {
                    añadirFichero(fichero, this.tabControl);
                }
            }
        }
        #endregion

        #region Funciones generales para tratar los metadatos
        public static void añadirFichero(String pathFichero, TabControl tc)
        {
            //Para no tener pestañas con nombres muy largos
            if (Path.GetFileName(pathFichero).Length > 20)
                tc.TabPages.Add(Path.GetFileName(pathFichero).Substring(0, 17) + "...");
            else
                tc.TabPages.Add(Path.GetFileName(pathFichero));
            ListBox lb = new ListBox();
            lb.Top = 6;
            lb.Left = 6;
            lb.Width = tc.TabPages[tc.TabCount - 1].Width - 12;
            lb.Height = tc.TabPages[tc.TabCount - 1].Height - 12;
            lb.Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left;
            lb.HorizontalScrollbar = true;
            lb.ContextMenuStrip = Program.FormPrincipalInstance.contextMenuStripListView;
            tc.SelectedIndex = tc.TabCount - 1;
            tc.TabPages[tc.TabCount - 1].ToolTipText = pathFichero;
            tc.TabPages[tc.TabCount - 1].ImageIndex = 0;
            tc.TabPages[tc.TabCount - 1].Controls.Add(lb);
            Program.FormPrincipalInstance.ficheros.Add(pathFichero);
            switch(Path.GetExtension(pathFichero)){
                case ".sxw":
                case ".odt":
                case ".ods":
                case ".odg":
                case ".odp":
                    OODocument.añadirFichero(pathFichero, tempFolder, lb, false, OODocument.EscribeEnListBox, OODocument.EscribeEnListBoxRecursiva);
                    break;
            }
        }

        /*Devuelve en una cadena la información mas interesante de un fichero*/
        public static string resumeFichero(String pathFichero)
        {
            switch (Path.GetExtension(pathFichero))
            {
                case ".sxw":
                case ".odt":
                case ".ods":
                case ".odg":
                case ".odp":
                    StringWriter sw = new StringWriter();
                    sw.WriteLine("File:   " + pathFichero);
                    OODocument.añadirFichero(pathFichero, tempFolder, sw, true, OODocument.EscribeEnCadena, OODocument.EscribeEnCadenaRecursiva);
                    return sw.ToString();
                default: 
                    return "";
            }
        }

        public static void borrarMetadatosDeFichero(String pathFichero, ListBox lb)
        {
            if (lb != null)
                lb.Items.Clear();
            switch (Path.GetExtension(pathFichero))
            {
                case ".sxw":
                case ".odt":
                case ".ods":
                case ".odg":
                case ".odp":
                    OODocument.borrarMetadatosDeFichero(pathFichero, tempFolder, Program.FormOpcionesInstance.OOPlantilla, lb, OODocument.EscribeEnListBox); 
                    break;
            }
        }
        
        public static void añadirDirectorio(String pathDirectorio, TabControl tc)
        {
            List<String> ls = new List<String>();
            foreach (String extension in Program.FormPrincipalInstance.extensiones.Split(';'))
            {
                ls.AddRange(Directory.GetFiles(pathDirectorio, extension));
            }
            DialogResult dr;
            if (ls.Count <= 10 ||
                (dr = MessageBox.Show(String.Format(MSG_TOO_MANY_FILES, ls.Count.ToString()),
                                                   MSG_TOO_MANY_FILES_TITLE,
                                                   MessageBoxButtons.YesNoCancel,
                                                   MessageBoxIcon.Information)) == DialogResult.No)
            {
                foreach (String path in ls)
                {
                    añadirFichero(path, tc);
                }
            }
            else
            {
                if (dr != DialogResult.Cancel)
                {
                    borrarMetadatosCarpeta(pathDirectorio, tc);
                }
            }
        }

        public static void borrarMetadatosCarpeta(String path, TabControl tc)
        {
            Program.FormPrincipalInstance.ficheros.Add(path);
            tc.TabPages.Add(MSG_CLEANING_METADATA);
            tc.SelectedIndex = tc.TabCount - 1;

            ProgressBar pb = new ProgressBar();
            pb.Top = 20;
            pb.Left = 10;
            pb.Width = tc.TabPages[tc.TabCount - 1].Width - 20;
            pb.Height = 20;
            pb.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left;
            pb.Step = 1;
            tc.TabPages[tc.TabCount - 1].Controls.Add(pb);

            Label l = new Label();
            l.Top = 50;
            l.Left = 20;
            l.Width = tc.TabPages[tc.TabCount - 1].Width - 120;
            l.Height = 13;
            l.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left;
            l.AutoEllipsis = true;
            tc.TabPages[tc.TabCount - 1].Controls.Add(l);

            Button b = new Button();
            b.Top = l.Top;
            b.Width = 80;
            b.Left = pb.Left + pb.Width - b.Width;
            b.Height = 23;
            b.Click += Program.FormPrincipalInstance.buttonCancel_Click;
            b.Anchor = AnchorStyles.Top | AnchorStyles.Left;
            b.Text = MSG_CANCEL;
            tc.TabPages[tc.TabCount - 1].Controls.Add(b);

            Program.FormPrincipalInstance.backgroundWorkerBorrarCarpeta.RunWorkerAsync(tc.TabPages[tc.TabCount - 1]);
        }

        public static void resumirMetadatosCarpeta(String[] paths, TabControl tc)
        {
            Program.FormPrincipalInstance.ficheros.Add("");
            tc.TabPages.Add(MSG_SUMMARIZING_FOLDER);
            tc.SelectedIndex = tc.TabCount - 1;

            ProgressBar pb = new ProgressBar();
            pb.Top = 20;
            pb.Left = 10;
            pb.Width = tc.TabPages[tc.TabCount - 1].Width - 20;
            pb.Height = 20;
            pb.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left;
            pb.Step = 1;
            tc.TabPages[tc.TabCount - 1].Controls.Add(pb);

            Label l = new Label();
            l.Top = 50;
            l.Left = 20;
            l.Width = tc.TabPages[tc.TabCount - 1].Width - 120;
            l.Height = 13;
            l.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left;
            l.AutoEllipsis = true;
            tc.TabPages[tc.TabCount - 1].Controls.Add(l);

            Button b = new Button();
            b.Top = l.Top;
            b.Width = 80;
            b.Left = pb.Left + pb.Width - b.Width;
            b.Height = 23;
            b.Click += Program.FormPrincipalInstance.buttonCancel_Click;
            b.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left;
            b.Text = MSG_CANCEL;
            tc.TabPages[tc.TabCount - 1].Controls.Add(b);

            Program.FormPrincipalInstance.backgroundWorkerResumirCarpeta.RunWorkerAsync(new Object[]{tc.TabPages[tc.TabCount - 1], 
                                                                                                     paths});
        }
        #endregion

        private void englishToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
            Program.LoadLanguage();
            Program.FormOpcionesInstance.SaveConfig();
        }

        private void spanishToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("es-ES");
            Program.LoadLanguage();
            Program.FormOpcionesInstance.SaveConfig();
        }
    }
}
